package org.mozhu.mboot.core.web.filter;

import org.mozhu.mboot.core.web.HttpServletRequestSkipper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

/**
 * 根据J2EE规范，web.xml中配置的filter只能通过url-pattern来判断是否由该filter执行 <br/>
 * <br/>
 * 此BaseFilter提供基本的黑名单和白名单过滤的功能 <br/>
 * <br/>
 * 1.黑名单功能 满足某些特定条件的url，不执行filter真正的逻辑 <br/>
 * 2.白名单功能 只有满足某些特定条件的url，才执行filter真正的逻辑 <br/>
 * 以此来扩展某些开源框架所提供Filter的性能，如OpenSessionInViewFilter. <br/>
 * <br/>
 * <p>
 * <p>
 * 配置黑名单(exclutions)（若未指定黑名单，则默认所有请求路径都不会被拦截）
 * </p>
 * <p>
 * <pre>
 * &lt;init-param&gt;
 * 	&lt;param-name&gt;exclusions&lt;/param-name&gt;
 * 	&lt;!-- param-value 分隔符支持 逗号 分号 空格 换行符 --&gt;
 * 	&lt;param-value&gt;/static/**,*.js,*.css,*.jpg,*.gif,*.png,*.ico&lt;/param-value&gt;
 * &lt;/init-param&gt;
 * </pre>
 * <p>
 * <p>
 * 配置白名单(inclusions)（若未指定白名单，则默认所有请求路径都会执行）
 * </p>
 * <p>
 * <pre>
 * &lt;init-param&gt;
 * 	&lt;param-name&gt;inclusions&lt;/param-name&gt;
 * 	&lt;!-- param-value 分隔符支持 逗号 分号 空格 换行符 --&gt;
 * 	&lt;param-value&gt;/static/**,*.js,*.css,*.jpg,*.gif,*.png,*.ico&lt;/param-value&gt;
 * &lt;/init-param&gt;
 * </pre>
 *
 * @author ZhangPei
 */
public abstract class BaseFilter implements Filter {

    protected final Logger logger = LoggerFactory.getLogger(getClass());

    // 逗号 分号 空格 换行符
    private final static String URL_PATTERN_SEPERATOR = "[,;\\s]";

    protected final static String PAREMETER_KEY_DELEGATE_CLASS = "delegateClass";

    protected final static String PAREMETER_KEY_EXCLUSIONS = "exclusions";
    protected final static String PAREMETER_KEY_INCLUSIONS = "inclusions";

    private FilterConfig filterConfig;

    private HttpServletRequestSkipper skipper;

    public BaseFilter() {
    }

    public BaseFilter(HttpServletRequestSkipper skipper) {
        this.skipper = skipper;
    }

    @Override
    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        if (skipper.needSkip(httpRequest)) {
            logger.debug("Skip Filter [{}], SevletPath [{}]", getFilterName(), httpRequest.getServletPath());
            filterChain.doFilter(request, response);
            return;
        }
        doFilter(httpRequest, httpResponse, filterChain);
    }

    protected String getFilterName() {
        return this.getClass().getName();
    }

    protected abstract void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
        this.init();
    }

    protected void init() throws ServletException {
        if (this.skipper == null) {
            Set<String> exclusions = getNonBlankParameterValues(filterConfig, PAREMETER_KEY_EXCLUSIONS);
            Set<String> inclusions = getNonBlankParameterValues(filterConfig, PAREMETER_KEY_INCLUSIONS);
            this.skipper = new HttpServletRequestSkipper(exclusions, inclusions);
        }
    }

    protected String getParameterValue(FilterConfig filterConfig, String paremeterKey) {
        return filterConfig.getInitParameter(paremeterKey);
    }

    protected String getParameterValue(FilterConfig filterConfig, String paremeterKey, String defautValue) {
        String value = getParameterValue(filterConfig, paremeterKey);
        return value == null ? defautValue : value;
    }

    protected Set<String> getNonBlankParameterValues(FilterConfig filterConfig, String paremeterKey) {
        Set<String> parameterValues = new HashSet<String>();
        String parameterValueString = filterConfig.getInitParameter(paremeterKey);
        if (StringUtils.hasText(parameterValueString)) {
            String[] values = parameterValueString.split(URL_PATTERN_SEPERATOR);
            for (String value : values) {
                if (StringUtils.hasText(value)) {
                    parameterValues.add(value.trim());
                }
            }
        }
        return parameterValues;
    }

    protected FilterConfig getFilterConfig() {
        return filterConfig;
    }

}
